
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//#include <malloc.h>
#include "list.h"
#include "cutils.h"
#include "quickjs.h"

#include "tlsf.h"

//JS_LIMB_BITS
//mp_sub


/* binary object writer & reader */

//#pragma region MySection

//#pragma endregion


#define HEAP_SIZE  (256 * 1024)
static uint8_t heap_area[HEAP_SIZE];
static tlsf_t tlsf;

typedef struct {
    size_t free_size;
} free_info_t;

/* 回调函数，用于统计空闲块 */
static void pool_walker(void* ptr, size_t size, int used, void* user)
{
    free_info_t* info = (free_info_t*)user;
    if (!used) {
        info->free_size += size;
    }
}

/* 获取 TLSF 剩余可用空间 */
size_t tlsf_get_free_size()
{
    free_info_t info = {0};
    tlsf_walk_pool(tlsf_get_pool(tlsf), pool_walker, &info);
    return info.free_size;
}
// void *my_malloc(size_t size) {
//     return tlsf_malloc(tlsf, size);
// }

// void my_free(void *p) {
//     tlsf_free(tlsf, p);
// }

// void* tlsf_realloc(tlsf_t tlsf, void* ptr, size_t size);

int malloc_count;
int malloc_size;

#define MALLOC_OVERHEAD  8

/* default memory allocation functions with memory limitation */
static size_t js_def_malloc_usable_size(const void *ptr)
{
    return tlsf_block_size((void *)ptr);
}

static void *js_def_malloc(JSMallocState *s, size_t size)
{
    void *ptr;

    /* Do not allocate zero bytes: behavior is platform dependent */
    //assert(size != 0);

    if (s->malloc_size + size > s->malloc_limit)
        return NULL;

    ptr = tlsf_malloc(tlsf, size);//malloc(size);
    if (!ptr)
        return NULL;

    s->malloc_count++;
    s->malloc_size += tlsf_block_size(ptr) + MALLOC_OVERHEAD;
    
    malloc_count = s->malloc_count;
    malloc_size = s->malloc_size;
    return ptr;
}

static void js_def_free(JSMallocState *s, void *ptr)
{
    if (!ptr)
        return;

    s->malloc_count--;
    s->malloc_size -= tlsf_block_size(ptr) + MALLOC_OVERHEAD;
    tlsf_free(tlsf, ptr);
    malloc_count = s->malloc_count;
    malloc_size = s->malloc_size;
}

static void *js_def_realloc(JSMallocState *s, void *ptr, size_t size)
{
    size_t old_size;

    if (!ptr) {
        if (size == 0)
            return NULL;
        return js_def_malloc(s, size);
    }
    old_size = tlsf_block_size(ptr);
    if (size == 0) {
        s->malloc_count--;
        s->malloc_size -= old_size + MALLOC_OVERHEAD;
        tlsf_free(tlsf, ptr);
        return NULL;
    }
    if (s->malloc_size + size - old_size > s->malloc_limit)
        return NULL;

    ptr = tlsf_realloc(tlsf, ptr, size);
    if (!ptr)
        return NULL;

    s->malloc_size += tlsf_block_size(ptr) - old_size;
    
    malloc_count = s->malloc_count;
    malloc_size = s->malloc_size;

    return ptr;
}





static const JSMallocFunctions def_malloc_funcs = {
    js_def_malloc,
    js_def_free,
    js_def_realloc,
    js_def_malloc_usable_size,
};





// 一个简单的 console.log 实现
static JSValue js_console_log(JSContext *ctx, JSValueConst this_val,
                              int argc, JSValueConst *argv) {
    for (int i = 0; i < argc; i++) {
        const char *str = JS_ToCString(ctx, argv[i]);
        if (str) {
            printf("%s", str);
            JS_FreeCString(ctx, str);
        }
        if (i != argc - 1)
            printf(" ");
    }
    printf("\n");
    return JS_UNDEFINED;
}

void add_console(JSContext *ctx) {
    JSValue global = JS_GetGlobalObject(ctx);

    JSValue console = JS_NewObject(ctx);
    JS_SetPropertyStr(ctx, console, "log",
        JS_NewCFunction(ctx, js_console_log, "log", 1));

    JS_SetPropertyStr(ctx, global, "console", console);

    JS_FreeValue(ctx, global);
}















// 打印缩进
static void print_indent(int indent) {
    for (int i = 0; i < indent; i++) {
        putchar(' ');
    }
}

static void dump_value(JSContext *ctx, JSValue val, int indent) {
    if (JS_IsObject(val)) {
        if (JS_IsArray(ctx, val)) {
            // 数组
            uint32_t len;
            JSValue len_val = JS_GetPropertyStr(ctx, val, "length");
            JS_ToUint32(ctx, &len, len_val);
            JS_FreeValue(ctx, len_val);

            print_indent(indent);
            printf("[Array length=%u]\n", len);

            for (uint32_t i = 0; i < len; i++) {
                JSValue elem = JS_GetPropertyUint32(ctx, val, i);
                dump_value(ctx, elem, indent + 2);
                JS_FreeValue(ctx, elem);
            }
        } else {
            // 对象
            JSPropertyEnum *props;
            uint32_t plen;

            if (JS_GetOwnPropertyNames(ctx, &props, &plen, val,
                                       JS_GPN_STRING_MASK | JS_GPN_ENUM_ONLY) < 0) {
                return;
            }

            print_indent(indent);
            printf("{Object with %u props}\n", plen);

            for (uint32_t i = 0; i < plen; i++) {
                JSAtom atom = props[i].atom;
                const char *key = JS_AtomToCString(ctx, atom);

                JSValue prop_val = JS_GetProperty(ctx, val, atom);

                print_indent(indent + 2);
                printf("%s:\n", key);

                dump_value(ctx, prop_val, indent + 4);

                JS_FreeCString(ctx, key);
                JS_FreeValue(ctx, prop_val);
                JS_FreeAtom(ctx, atom);
            }

            js_free(ctx, props);
        }
    } else {
        // 基础类型
        const char *str = JS_ToCString(ctx, val);
        print_indent(indent);
        printf("%s\n", str ? str : "[non-printable]");
        if (str) JS_FreeCString(ctx, str);
    }
}




// C 函数：接收一个对象并遍历
static JSValue js_handle_data(JSContext *ctx, JSValueConst this_val,
                              int argc, JSValueConst *argv) {
    if (argc < 1) {
        return JS_ThrowTypeError(ctx, "expect argument");
    }

    dump_value(ctx, argv[0], 0);
    return JS_UNDEFINED;
}


//update windows (windows)
/*
{
  type: string/number
  title: string
  w: number
  h: number
  x: number
  y: number
  onClick: ()=>{}
  onMouseMove:
  ...

}
Windows:{
 Window[],
}

3D objs:
{

}

Files

js控制台window

文本框

*/



// 打印 JS 错误
static void dump_exception(JSContext *ctx, JSValue v) {
    const char *str = JS_ToCString(ctx, v);
    if (str) {
        printf("Exception: %s\n", str);
        JS_FreeCString(ctx, str);
    } else {
        printf("Exception (unknown)\n");
    }
}

static JSValue js_print(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
    for (int i = 0; i < argc; i++) {
        const char *str = JS_ToCString(ctx, argv[i]);
        if (str) {
            printf("%s", str);
            JS_FreeCString(ctx, str);
        }
        if (i < argc - 1)
            printf(" ");
    }
    printf("\n");
    return JS_UNDEFINED;
}

int main(int argc, char **argv) {
    size_t free;

    tlsf = tlsf_create_with_pool(heap_area, HEAP_SIZE);
    free = tlsf_get_free_size();printf("malloc_count: %d, %d, free:%d\n", malloc_count, malloc_size, free);


    malloc_count = 0;
    malloc_size = 0;
    // 创建运行时和上下文
    JSRuntime *rt = JS_NewRuntime(&def_malloc_funcs, NULL);
    if (!rt) {
        fprintf(stderr, "Failed to create JSRuntime\n");
        exit(1);
    }
    free = tlsf_get_free_size();printf("malloc_count: %d, %d, free:%d\n", malloc_count, malloc_size, free);
    JSContext *ctx = JS_NewContext(rt);
    if (!ctx) {
        fprintf(stderr, "Failed to create JSContext\n");
        JS_FreeRuntime(rt);
        exit(1);
    }
    add_console(ctx);
    free = tlsf_get_free_size();printf("malloc_count: %d, %d, free:%d\n", malloc_count, malloc_size, free);

    JSValue global_obj = JS_GetGlobalObject(ctx);
    JS_SetPropertyStr(
        ctx,
        global_obj,
        "print",
        JS_NewCFunction(ctx, js_print, "print", 1)  // 参数 1 = 最少参数个数
    );
    JS_FreeValue(ctx, global_obj);
    free = tlsf_get_free_size();printf("malloc_count: %d, %d, free:%d\n", malloc_count, malloc_size, free);


{
    // 注册全局函数 handleData
    JSValue global = JS_GetGlobalObject(ctx);
    JS_SetPropertyStr(ctx, global, "handleData", JS_NewCFunction(ctx, js_handle_data, "handleData", 1));
    JS_FreeValue(ctx, global);
}

    const char *script1 = 
            "handleData({"
        "  name: 'Alice',"
        "  age: 25,"
        "  active: true,"
        "  scores: [100, 98, 95],"
        "  address: { city: 'Paris', zip: '75000' }"
        "});"
        "var share = 'fdsa';"
        ;

    // 一段 JS 脚本
    const char *script = 
        // "var buf = new MyBuffer(10);"
        // "console.log('size =', buf.size());"
        "console.log('share:', share);"
        "print(/[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}/.test('fdsa'));"
        "print(/[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}/.test('fdsa@fdsa.fdsa'));"
        "try{"
        "fdsa();"
        "}catch(ex){"
        "print(ex);"
        "}"
        "var r = Math.random();"
        "print(r);"
        "var a = 1.5;"
        "var b = Math.round(a);"
        "print(b);"
        "let obj = JSON.parse(`{\"aa\":123.123}`);"
        "print(obj.aa);"
        "let aa = parseFloat(`123.123`);"
        "print(aa);"
        "const add = (a, b)=>{ return a + b + 1.2; };"
        "let result = add(3.12321, 4.2);"
        "print(`true:8.523210`);"
        "print(result);"
        "var temp1 = 1.21;"
        "var temp2 = 3.421;"
        "var temp = temp1 / temp2;"
        "print(`true:0.3536977491961415`);"
        "print(temp);"
        "result;";

    // 执行脚本
    JS_Eval(ctx, script1, strlen(script1), "<input>", JS_EVAL_TYPE_GLOBAL);


    free = tlsf_get_free_size();printf("malloc_count: %d, %d, free:%d\n", malloc_count, malloc_size, free);

    JSValue val = JS_Eval(ctx, script, strlen(script), "<input>", JS_EVAL_TYPE_GLOBAL);

    if (JS_IsException(val)) {
        dump_exception(ctx, JS_GetException(ctx));
    } else {
        float int_result;
        if (JS_ToFloat64(ctx, &int_result, val) == 0) {
            printf("Result from JS: %f\n", int_result);
        }

        int int_result2;
        if (JS_ToInt32(ctx, &int_result2, val) == 0) {
            printf("Result2 from JS: %d\n", int_result2);
        }
        
    }
    free = tlsf_get_free_size();printf("malloc_count: %d, %d, free:%d\n", malloc_count, malloc_size, free);

    JS_FreeValue(ctx, val);
    JS_FreeContext(ctx);
    free = tlsf_get_free_size();printf("malloc_count: %d, %d, free:%d\n", malloc_count, malloc_size, free);

    JS_FreeRuntime(rt);
    free = tlsf_get_free_size();printf("malloc_count: %d, %d, free:%d\n", malloc_count, malloc_size, free);
    return 0;
}










/*


#include "quickjs.h"

int mainx() {
    JSRuntime *rt = JS_NewRuntime();
    JSContext *ctx = JS_NewContext(rt);

    // 注册 class
    JS_NewClassID(&js_mybuffer_class_id);
    JS_NewClass(rt, js_mybuffer_class_id, &js_mybuffer_class);

    // 构造函数
    JSValue global = JS_GetGlobalObject(ctx);
    JSValue ctor = JS_NewCFunction(ctx, js_new_mybuffer, "MyBuffer", 1);
    JS_SetPropertyStr(ctx, ctor, "size", JS_NewCFunction(ctx, js_mybuffer_size, "size", 0));
    JS_SetPropertyStr(ctx, global, "MyBuffer", ctor);
    JS_FreeValue(ctx, global);

    // JS 代码：创建并使用对象
    const char *script =
        "let b = new MyBuffer(10);\n"
        "console.log('size =', b.size());\n";

    JS_Eval(ctx, script, strlen(script), "<input>", JS_EVAL_TYPE_GLOBAL);

    // 触发 GC
    JS_RunGC(rt);

    JS_FreeContext(ctx);
    JS_FreeRuntime(rt);
    return 0;
}

*/